import * as isomorphicFetch from 'isomorphic-fetch';
import {
    IApiError,
    authRely
} from '../index';
import { call } from 'redux-saga/effects';
import * as warning from 'warning';
import { Modal, message } from 'antd';

let defaultApiErrorHanlder: (error: IApiError) => boolean;

type ApiErrorHanlderType = typeof defaultApiErrorHanlder;

export const OFFLINE_FETCH_ERROR = {
    name: 'offline',
    message: '似乎连接不上服务器，部分需要联机的功能将停用',
    code: 0
} as IApiError;

export const registerDefaultApiErrorHandler = () => {
    registerApiErrorHandler(
        (error: IApiError): boolean => {
            const identityError = (error.code >= 110100 && error.code <= 110121) || error.code === 100121;

            if (window.__ELE__ && error.code === 0) {
                message.warning('似乎连接不上服务器，部分需要联机的功能将停用');

                return true;
            }

            if (error.code === 0) {
                message.warning(`${error.message}（${error.code}）`);

                return true;
            }

            Modal.error({
                title: error.name,
                content: `${error.message}（${error.code}）`,
                okText: identityError ? '重新登录' : '知道了',
                onOk: identityError ? authRely.logout : undefined
            });

            return true;
        }
    );
};

/**
 * 注册Api请求错误处理方法
 * 在使用 handleApiError 处理错误 或 在 saga 中使用 runApiSaga 中请求Api时发生错误时调用此出错处理方法
 * @param handler
 */
export const registerApiErrorHandler = (handler: ApiErrorHanlderType) => {
    defaultApiErrorHanlder = handler;
};

/**
 * 使用已注册的错误处理方法处理Api标准错误
 * @param error Api错误
 */
export const handleApiError = (error: IApiError) => {
    let handled: boolean = false;
    if (defaultApiErrorHanlder) {
        handled = defaultApiErrorHanlder(error);
    } else {
        warning(false, 'you must register A api error handler before use hanldeApiError func');
    }

    return handled;
};

export function* handleApiErrorViaMessage(error: any) {
    yield message.warning(`${error.message}（${error.code}）`);
}

/**
 * 在 saga 中包装包含 api 请求的处理过程
 * 当过程中的 api 请求出现错误时通过已注册的错误处理方法处理Api标准错误
 * @param func 包装包含 api 请求的处理过程
 * @param payload 需要附带的对象
 * @param onErrorFunc 当出现错误时需要额外处理的方法
 * @param silence 是否静默处理
 */
export function* runApiSaga(
    func: (payload?: any) => IterableIterator<any>,
    payload?: any,
    onErrorFunc?: (err: any) => IterableIterator<any>,
    silence?: boolean
) {
    try {
        yield call(func, payload);
    } catch (err) {
        let handled: boolean = false;

        if (err as IApiError) {
            if (err.code === 0) {
                err.name = '网络错误';
                err.message = '服务器好像在开小差...ummmmm....';
            }

            if (silence) {
                handled = true;
            } else {
                handled = handleApiError(err);
            }
        }

        if (onErrorFunc) {
            yield call(onErrorFunc, err);
        }

        if (!handled) {
            throw err;
        }
    }
}

export const getApiFetchPath = (
    apiPath: string,
    method?: 'GET' | 'POST' | 'PUT' | 'DELETE',
    requestData?: { [key: string]: any },
    overrideApiFullPath?: string
): string => {
    // 处理请求地址
    if (overrideApiFullPath === undefined) {
        let apiFullPath = `${__API_URL.endsWith('/') ? __API_URL : __API_URL + '/'}${apiPath}`;

        if (method === 'GET' && requestData) {
            const params: string[] = [];
            // tslint:disable-next-line:forin
            for (const key in requestData) {
                params.push(`${key}=${encodeURIComponent(requestData[key])}`);
            }
            if (params.length > 0) {
                apiFullPath = `${apiFullPath}${apiFullPath.indexOf('?') >= 0 ? '&' : '?'}${params.join('&')}`;
            }
        }

        return apiFullPath;
    } else {
        return overrideApiFullPath;
    }
};

export const getApiFetchOptions = (
    postData?: any,
    method?: 'GET' | 'POST' | 'PUT' | 'DELETE',
    customHeaders?: any
): any => {
    let headers: any = {
        'Content-Type': 'application/json'
    };
    if (undefined !== customHeaders) {
        headers = Object.assign({}, headers, customHeaders);
    }

    method = method || 'GET';

    // 处理请求
    const fetchOptions: any = {
        method,
        headers
    };
    if ('GET' !== method && undefined !== postData && null !== postData) {
        fetchOptions.body = JSON.stringify(postData);
    }

    return fetchOptions;
};

export const fetchFunc = (url: string, options?: any): Promise<any> =>
    new Promise((resolve, reject) => {
        if (window.__ELE__ && (!navigator.onLine) && (!__DEV__)) {
            reject(OFFLINE_FETCH_ERROR);

            return;
        }

        isomorphicFetch(url, options)
            .then((response) => {
                return response.json();
            }).then((ret) => {
                ret.code === 0 ? resolve(ret.data) : reject(ret.data);
            }).catch((error) => {
                reject({
                    name: 'Request failed',
                    message: error.message,
                    code: 0
                } as IApiError);
            });
    });

/**
 * 发送一个不带授权的JSON请求
 * @param apiPath [api_url]api/[apiPath]
 * @param method 请求方法
 * @param requestData 需要提交的参数，get时拼接为url，其他方式作为body提交
 * @param overrideApiFullPath 用于不遵守 apiPath 时使用的完整请求路径
 * @param customHeaders 需要附加的自定义请求头
 */
export const apiFetch = (
    apiPath: string,
    method?: 'GET' | 'POST' | 'PUT' | 'DELETE',
    requestData?: { [key: string]: any },
    overrideApiFullPath?: string,
    customHeaders?: any
): Promise<any> => {
    return new Promise((resolve, reject) => {
        // 处理请求地址
        const apiFullPath = getApiFetchPath(apiPath, method, requestData, overrideApiFullPath);

        // 处理请求
        const fetchOptions: any = getApiFetchOptions(requestData, method, customHeaders);

        // 执行请求
        fetchFunc(apiFullPath, fetchOptions)
            .then((data) => {
                resolve(data);
            }).catch((error) => {
                reject(error);
            });
    });
};
